"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const path_1 = require("path");
const fs_1 = require("fs");
const lodash_1 = require("lodash");
const chokidar = require('chokidar');
const bootstrap = ({ packageName }) => __awaiter(void 0, void 0, void 0, function* () {
    if (packageName) {
        let cd_dir = path_1.resolve(`./node_modules/${packageName}`);
        if (fs_1.existsSync(cd_dir)) {
            process.chdir(cd_dir);
        }
        else {
            cd_dir = path_1.resolve(`./packages/${packageName}`);
            if (fs_1.existsSync(cd_dir)) {
                process.chdir(cd_dir);
            }
        }
    }
    // Do this as the first thing so that any code reading it knows the right env.
    process.env.BABEL_ENV = 'development';
    process.env.NODE_ENV = 'development';
    require('../config/env');
    const paths = require('../config/paths');
    const fs = require('fs');
    const compileEnvFile = path_1.resolve(paths.appConfig, 'compileStartEnv.js');
    if (fs.existsSync(compileEnvFile)) {
        Object.assign(process.env, require(compileEnvFile));
    }
    // Makes the script crash on unhandled rejections instead of silently
    // ignoring them. In the future, promise rejections that are not handled will
    // terminate the Node.js process with a non-zero exit code.
    process.on('unhandledRejection', (err) => {
        throw err;
    });
    // Ensure environment variables are read.
    const chalk = require('react-dev-utils/chalk');
    const webpack = require('webpack');
    const WebpackDevServer = require('webpack-dev-server');
    const clearConsole = require('react-dev-utils/clearConsole');
    const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
    const { choosePort, createCompiler, prepareProxy, prepareUrls, } = require('react-dev-utils/WebpackDevServerUtils');
    const openBrowser = require('react-dev-utils/openBrowser');
    const configFactory = require('../config/webpack.config');
    const createDevServerConfig = require('../config/webpackDevServer.config');
    const useYarn = fs.existsSync(paths.yarnLockFile);
    const isInteractive = process.stdout.isTTY;
    yield require('./transpile').transpileCommonIfChange();
    // Warn and crash if required files are missing
    if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
        process.exit(1);
    }
    // Tools like Cloud9 rely on this.
    const DEFAULT_PORT = parseInt(`${process.env.PORT}`, 10) || 8000;
    const HOST = process.env.HOST || '0.0.0.0';
    if (process.env.HOST) {
        console.log(chalk.cyan(`Attempting to bind to HOST environment variable: ${chalk.yellow(chalk.bold(process.env.HOST))}`));
        console.log(`If this was unintentional, check that you haven't mistakenly set it in your shell.`);
        console.log(`Learn more here: ${chalk.yellow('http://bit.ly/CRA-advanced-config')}`);
        console.log();
    }
    const DISABLE_BUILD_DLL = process.env.DISABLE_BUILD_DLL === 'true';
    if (!DISABLE_BUILD_DLL && !fs.existsSync(paths.appDll)) {
        yield require('./build-dll').default();
        process.env.BABEL_ENV = 'development';
        process.env.NODE_ENV = 'development';
    }
    // We require that you explictly set browsers and do not fall back to
    // browserslist defaults.
    const { checkBrowsers } = require('react-dev-utils/browsersHelper');
    return checkBrowsers(paths.appPath, isInteractive)
        .then(() => {
        // We attempt to use the default port but if it is busy, we offer the user to
        // run on a different port. `choosePort()` Promise resolves to the next free port.
        return choosePort(HOST, DEFAULT_PORT);
    })
        .then((port) => __awaiter(void 0, void 0, void 0, function* () {
        if (port == null) {
            // We have not found a port.
            return;
        }
        let config = {};
        const ignore = [
            'node_modules/**',
            'packages/**',
            'dist/**',
            '.smock/**',
            '_smock/**',
        ];
        let beforeMiddlewares = [];
        let mockWatcher = null;
        let addMockWatcher = null;
        let mockFiles = [];
        try {
            if (process.env.UMI_UI !== 'none') {
                const getUmiService = require('./umi-scripts/umiService')
                    .getUmiService;
                const createMiddleware = require('@umijs/preset-built-in/lib/plugins/commands/dev/mock/createMiddleware')
                    .default;
                const { getMockData, } = require('@umijs/preset-built-in/lib/plugins/commands/dev/mock/utils');
                const BabelRegister = require('@umijs/utils/lib/BabelRegister/BabelRegister')
                    .default;
                let lastEnvMock = process.env.MOCK;
                process.env.MOCK = 'none';
                const umiService = getUmiService({});
                const api = (yield umiService.run({
                    name: 'cpaas-start',
                })).api;
                const entryCode = (yield api.applyPlugins({
                    key: 'addEntryCode',
                    type: api.ApplyPluginsType.add,
                    initialValue: [],
                })).join('\r\n');
                const entryCodeAhead = (yield api.applyPlugins({
                    key: 'addEntryCodeAhead',
                    type: api.ApplyPluginsType.add,
                    initialValue: [],
                })).join('\r\n');
                let webpackChainConfig = new (require('webpack-chain'))();
                yield api.applyPlugins({
                    type: api.ApplyPluginsType.modify,
                    key: 'chainWebpack',
                    initialValue: webpackChainConfig,
                });
                const babelrc = yield api.applyPlugins({
                    type: api.ApplyPluginsType.modify,
                    key: 'modifyBabelOpts',
                    initialValue: require('../config/base/getBabelrc')(),
                });
                const beforeMiddlewareList = yield api.applyPlugins({
                    key: 'addBeforeMiddewares',
                    type: api.ApplyPluginsType.add,
                    initialValue: [],
                    args: {},
                });
                beforeMiddlewares.push(...beforeMiddlewareList);
                config = configFactory('development', babelrc);
                if (!lodash_1.isArray(config.entry)) {
                    config.entry = [config.entry];
                }
                api.writeTmpFile({
                    path: 'entryCodeAhead.ts',
                    content: entryCodeAhead,
                });
                config.entry.unshift(path_1.join(api.paths.absTmpPath, 'entryCodeAhead.ts'));
                api.writeTmpFile({
                    path: 'entryCode.ts',
                    content: entryCode,
                });
                config.entry.push(path_1.join(api.paths.absTmpPath, 'entryCode.ts'));
                const umiWebpackConfig = webpackChainConfig.toConfig();
                if (umiWebpackConfig.plugins &&
                    umiWebpackConfig.plugins[0] &&
                    umiWebpackConfig.plugins[0].constructor.name ===
                        'CompileStatusWebpackPlugin') {
                    config.plugins.push(umiWebpackConfig.plugins[0]);
                }
                const babelRegister = new BabelRegister();
                const registerBabel = (files) => {
                    babelRegister.setOnlyMap({
                        key: 'mock',
                        value: [...files],
                    });
                };
                const mockResult = getMockData({
                    cwd: paths.appPath,
                    ignore,
                    registerBabel,
                });
                mockFiles = mockResult;
                const { middleware, watcher } = createMiddleware(Object.assign(Object.assign({}, mockResult), { updateMockData: () => {
                        const result = getMockData({
                            cwd: paths.appPath,
                            ignore,
                            registerBabel,
                        });
                        mockFiles = result;
                        return result;
                    } }));
                mockWatcher = watcher;
                beforeMiddlewares.push(middleware);
                process.env.MOCK = lastEnvMock;
            }
            else {
                config = configFactory('development');
            }
        }
        catch (error) {
            console.error(error);
            config = configFactory('development');
        }
        const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
        const appName = require(paths.appPackageJson).name;
        const urls = prepareUrls(protocol, HOST, port);
        // Create a webpack compiler that is configured with custom messages.
        const compiler = createCompiler({
            webpack,
            config,
            appName,
            urls,
            useYarn,
        });
        // compiler.hooks.done.tap('done', async () => {
        //   console.log('mockWatcher', mockWatcher);
        // });
        if (mockWatcher) {
            // Initialize watcher.
            addMockWatcher = chokidar.watch('./src/**/_mock.[jt]s', {
                ignore,
                persistent: true,
                cwd: paths.appPath,
            });
            // @ts-ignore
            addMockWatcher.on('add', (path) => {
                if (mockFiles.mockPaths.every((filePath) => !filePath.includes(path)) &&
                    (path.endsWith('_mock.js') || path.endsWith('_mock.ts'))) {
                    const filePath = path_1.join(paths.appPath, path);
                    console.log('add mock file path:', filePath);
                    // @ts-ignore
                    mockWatcher.add(filePath);
                    // @ts-ignore
                    mockWatcher.emit('all', path, filePath);
                }
            });
        }
        // Load proxy config
        const proxySetting = require(paths.appPackageJson).proxy;
        const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
        // Serve webpack assets generated by the compiler over a web server.
        const serverConfig = createDevServerConfig(proxyConfig, urls.lanUrlForConfig, beforeMiddlewares);
        const devServer = new WebpackDevServer(compiler, serverConfig);
        // Launch WebpackDevServer.
        devServer.listen(port, HOST, (err) => {
            if (err) {
                return console.log(err);
            }
            if (isInteractive) {
                clearConsole();
            }
            console.log(chalk.cyan('Starting the development server...\n'));
            openBrowser(urls.localUrlForBrowser);
        });
        ['SIGINT', 'SIGTERM'].forEach(function (sig) {
            process.on(sig, function () {
                return __awaiter(this, void 0, void 0, function* () {
                    if (addMockWatcher) {
                        // @ts-ignore
                        yield addMockWatcher.close();
                    }
                    devServer.close();
                    process.exit();
                });
            });
        });
    }))
        .catch((err) => {
        // if (err && err.message) {
        console.error(err);
        // }
        process.exit(1);
    });
});
exports.default = bootstrap;
